package com.labofjet.common.util;

import com.labofjet.common.dto.ContextDto;
import com.labofjet.common.dto.PageDto;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * mongo查询工具类
 */
public class MongoQueryUtils {

    private Query query;
    private Criteria criteria;
    private Map<String, Object> params;
    private MongoTemplate mongoTemplate;

    private MongoQueryUtils(Map<String, Object> params, MongoTemplate mongoTemplate) {
        query = new Query();
        criteria = new Criteria();
        query.addCriteria(criteria);
        this.params = params;
        this.mongoTemplate = mongoTemplate;
    }

    public static MongoQueryUtils getInstance(Map<String, Object> params) {
        return new MongoQueryUtils(params, null);
    }

    public static MongoQueryUtils getInstance(Map<String, Object> params, MongoTemplate mongoTemplate) {
        return new MongoQueryUtils(params, mongoTemplate);
    }

    public static MongoQueryUtils getInstance(ContextDto contextDto) {
        return new MongoQueryUtils(contextDto.getRequestParams(), null);
    }


    public static MongoQueryUtils getInstance(ContextDto contextDto, MongoTemplate mongoTemplate) {
        return new MongoQueryUtils(contextDto.getRequestParams(), mongoTemplate);
    }


    public MongoQueryUtils and(Criteria... c) {
        criteria.andOperator(c);
        return this;
    }

    public MongoQueryUtils and(String key) {
        Object o = params.get(key);
        if (o != null && StringUtils.isNotEmpty(String.valueOf(o))) {
            criteria.and(key).is(o);
        }
        return this;
    }

    public MongoQueryUtils like(String key) {
        Object o = params.get(key);
        if (o != null && StringUtils.isNotEmpty(String.valueOf(o))) {
            criteria.and(key).regex(".*" + o + ".*");
            ;
        }
        return this;
    }

    public MongoQueryUtils in(String key) {
        Collection<?> o = (Collection<?>) params.get(key);
        if (CollectionUtils.isNotEmpty(o)) {
            criteria.and(key).in(o);
        }
        return this;
    }

    public MongoQueryUtils in(String key, String fieldName) {
        Collection<?> o = (Collection<?>) params.get(key);
        if (CollectionUtils.isNotEmpty(o)) {
            criteria.and(fieldName).in(o);
        }
        return this;
    }

    public MongoQueryUtils start(String key) {
        Object o = params.get(key);
        if (o != null && StringUtils.isNotEmpty(String.valueOf(o))) {
            criteria.and(key.endsWith("Start") ? key.substring(0, key.length() - 5) : key).gte(String.valueOf(o));
        }
        return this;
    }

    public MongoQueryUtils end(String key) {
        Object o = params.get(key);
        if (o != null && StringUtils.isNotEmpty(String.valueOf(o))) {
            criteria.and(key.endsWith("End") ? key.substring(0, key.length() - 3) : key).lte(String.valueOf(o));
        }
        return this;
    }

    public MongoQueryUtils start(String key, String fieldName) {
        Object o = params.get(key);
        if (o != null && StringUtils.isNotEmpty(String.valueOf(o))) {
            criteria.and(fieldName).gte(String.valueOf(o));
        }
        return this;
    }

    public MongoQueryUtils end(String key, String fieldName) {
        Object o = params.get(key);
        if (o != null && StringUtils.isNotEmpty(String.valueOf(o))) {
            criteria.and(fieldName).lte(String.valueOf(o));
        }
        return this;
    }

    public MongoQueryUtils paging() {
        this.sort();
        String ps = StringHelper.defaultIfEmpty(params.get("pageSize"), null);
        String pn = StringHelper.defaultIfEmpty(params.get("pageNum"), null);
        if (ps == null || pn == null) {
            return this;
        }

        int pageSize = NumberUtils.toInt(ps);
        int pageNum = NumberUtils.toInt(pn);
        int skip = (pageNum - 1) * pageSize;
        query.skip(skip);// skip相当于从那条记录开始
        query.limit(pageSize);// 从skip开始,取多少条记录
        return this;
    }

    public MongoQueryUtils sort() {
        if (params.get("sortName") != null && StringUtils.isNotBlank(params.get("sortName").toString())) {
            StringBuilder b = new StringBuilder();
            List<String> sortNames = Arrays.asList(params.get("sortName").toString().split(","));
            List<String> sortOrders = Arrays.asList(params.get("sortOrder").toString().split(","));
            for (int i = 0; i < sortNames.size(); i++) {
                query.with(new Sort(Sort.Direction.fromString(sortOrders.get(i)), sortNames.get(i)));
            }
        }
        return this;
    }

    public <T> PageDto<T> find(Class<T> cls) {
        List<T> list = mongoTemplate.find(query, cls);
        long count = mongoTemplate.count(query, cls);
        PageDto<T> pageInfo = new PageDto<>(list);
        pageInfo.setTotal(count);
        return pageInfo;
    }

    public <Src, Tgt> PageDto<Tgt> find(Class<Src> src, Class<Tgt> tgt) {
        List<Src> list = mongoTemplate.find(query, src);
        long count = mongoTemplate.count(query, src);
        List<Tgt> tgtList = ConverterUtils.convertList(list, tgt);
        PageDto<Tgt> pageInfo = new PageDto<>(tgtList);
        pageInfo.setTotal(count);
        return pageInfo;
    }


    public Query getQuery() {
        return query;
    }

    public void setQuery(Query query) {
        this.query = query;
    }

    public Criteria getCriteria() {
        return criteria;
    }

    public void setCriteria(Criteria criteria) {
        this.criteria = criteria;
    }

    public Map<String, Object> getParams() {
        return params;
    }

    public void setParams(Map<String, Object> params) {
        this.params = params;
    }

    public MongoTemplate getMongoTemplate() {
        return mongoTemplate;
    }

    public void setMongoTemplate(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }
}

